#ifndef __CRYGAMESPYFRIENDS_H__
#define __CRYGAMESPYFRIENDS_H__

#pragma once

#include "CryGameSpyLobby.h"

#if USE_CRYLOBBY_GAMESPY

#include "ICryFriendsManagement.h"
#include "Lobby/CryFriends.h"

struct SCryGameSpyUserID;
#define GAMESPY_MESSAGE_STRING_LEN (256)

class CCryGameSpyFriends: public CCryFriends, public ICryGameSpyLobbyComponent
{
public:
															CCryGameSpyFriends(CCryLobby* pLobby, CCryLobbyService* pService);

					void								Tick(CTimeValue tv);

					// ICryFriends
	virtual	ECryLobbyError			FriendsGetFriendsList(uint32 user, uint32 start, uint32 num, CryLobbyTaskID* pTaskID, CryFriendsGetFriendsListCallback pCb, void* pCbArg);
	virtual ECryLobbyError			FriendsSendGameInvite(uint32 user, CrySessionHandle h, CryUserID* pUserIDs, uint32 numUserIDs, CryLobbyTaskID* pTaskID, CryFriendsCallback pCb, void* pCbArg);
					// ~ICryFriends

					// ICryGameSpyLobbyComponent
	virtual void								OnGameSpyObjectAvailable( EGameSpyObjectBit object );
	virtual void								OnGameSpyObjectUnavailable( EGameSpyObjectBit object );
	virtual void								OnGameSpyObjectCantMakeAvailable( EGameSpyObjectBit object );
					// ~ICryGameSpyLobbyComponent

					void								InitialiseCallbacks(GPConnection* pGPConnection);
					void								LateInitialise(GPProfile gpProfile);
					uint64							GetInviteSessionID(GPConnection* pGPConnection, GPProfile gpProfile);

					ECryLobbyError			FriendsGetNamesList(uint32 user, CryUserID* pUserIDs, uint32 numUserIDs, CryLobbyTaskID* pTaskID, CryFriendsGetFriendsListCallback pCb, void* pCbArg);

					ECryLobbyError			FriendsSendFriendRequest( GPProfile profile, const char* pReason );
					ECryLobbyError			FriendsAcceptFriendRequest( GPProfile profile );
					ECryLobbyError			FriendsDenyFriendRequest( GPProfile profile );
					ECryLobbyError			FriendsRevokeFriendStatus( GPProfile profile );

					uint32							GetFriendCountInSession( const char* pUrl );
					ECryLobbyError			GetFriendStatus( GPProfile profile, SFriendStatusInfo* pInfo );

private:
	enum ETask
	{
		eT_FriendsGetFriendsList,
		eT_FriendsSendGameInvite,
		eT_FriendsGetNamesList
	};

	struct  STask : public CCryFriends::STask
	{
		CTimeValue								m_timeStarted;
		bool											m_haveGPConnection;
		bool											m_taskStarted;
	};

	enum EBuddyBit
	{
		eBB_SentForwardRequest = BIT( 0 ),
		eBB_ReceivedForwardRequest = BIT( 1 ),
		eBB_SentForwardAccept = BIT( 2 ),
		eBB_ReceivedForwardAccept = BIT( 3 ),
		eBB_SentReverseRequest = BIT( 4 ),
		eBB_ReceivedReverseRequest = BIT( 5 ),
		eBB_SentReverseAccept = BIT( 6 ),
		eBB_ReceivedReverseAccept = BIT( 7 ),
		eBB_SetupOriginateMask = eBB_SentForwardRequest | eBB_ReceivedReverseAccept | eBB_ReceivedReverseRequest | eBB_SentForwardAccept,
		eBB_SetupAnswerMask = eBB_ReceivedForwardRequest | eBB_SentReverseAccept | eBB_SentReverseRequest | eBB_ReceivedForwardAccept,
		eBB_TestShouldSendForwardAcceptMask = eBB_SentForwardRequest | eBB_ReceivedReverseRequest | eBB_SentForwardAccept,
		eBB_ShouldSendForwardAccept = eBB_SentForwardRequest | eBB_ReceivedReverseRequest,
		eBB_ExpectingForwardAccept = eBB_ReceivedForwardRequest | eBB_SentReverseAccept | eBB_SentReverseRequest,
		eBB_SentForwardRevoke = BIT( 8 ),
		eBB_ReceivedForwardRevoke = BIT ( 9 ),
		eBB_SentReverseRevoke = BIT( 10 ),
		eBB_ReceivedReverseRevoke = BIT( 11 ),
		eBB_RevokeOriginateMask = eBB_SentForwardRevoke | eBB_ReceivedReverseRevoke,
		eBB_RevokeAnswerMask = eBB_ReceivedForwardRevoke | eBB_SentReverseRevoke,
		eBB_IsBuddy = BIT( 12 ),
		eBB_IsReverseBuddy = BIT( 13 ),
		eBB_InActiveQueue = BIT( 14 )
	};

	struct SGameSpyProfile
	{
		SGameSpyProfile() : m_status(GP_OFFLINE), m_lastUpdateTimestamp(0), m_buddyBits( 0 )
		{
		}

		CryFixedStringT<GP_STATUS_STRING_LEN>				m_statusString;
		CryFixedStringT<GP_LOCATION_STRING_LEN>			m_locationString;
		CryFixedStringT<GAMESPY_MESSAGE_STRING_LEN>	m_message;
		CryFixedStringT<GAMESPY_MESSAGE_STRING_LEN>	m_utmMessage;
		CryFixedStringT<GP_LOCATION_STRING_LEN>			m_invite;
		CryFixedStringT<CRYLOBBY_USER_NAME_LENGTH>	m_nickname;
		GPEnum		m_status;
		uint32		m_lastUpdateTimestamp;
		uint32		m_buddyBits;
	};

	typedef std::map<GPProfile, SGameSpyProfile> TProfileMap;
	typedef TProfileMap::value_type TProfileMapElement;
	typedef TProfileMap::iterator TProfileMapIterator;

					ECryLobbyError			StartTask(ETask etask, bool startRunning, uint32 user, CryFriendsTaskID* pFTaskID, CryLobbyTaskID* pLTaskID, CrySessionHandle h, void* pCb, void* pCbArg);
					void								StartTaskRunning(CryFriendsTaskID fTaskID);
					void								EndTask(CryFriendsTaskID fTaskID);
					void								StopTaskRunning(CryFriendsTaskID fTaskID);

					void								StartFriendsGetFriendsList(CryFriendsTaskID fTaskID);
					void								TickFriendsGetFriendsList(CryFriendsTaskID fTaskID);
					void								EndFriendsGetFriendsList(CryFriendsTaskID fTaskID);

					void								StartFriendsGetNamesList(CryFriendsTaskID fTaskID);
					void								TickFriendsGetNamesList(CryFriendsTaskID fTaskID);
					void								EndFriendsGetNamesList(CryFriendsTaskID fTaskID);

					void								StartFriendsSendGameInvite(CryFriendsTaskID fTaskID);
					void								TickFriendsSendGameInvite(CryFriendsTaskID fTaskID);

					CryFriendsTaskID		GetTaskID(GPConnection* pGPConnection, GPProfile gpProfile);
					CCryGameSpyLobbyService*	GetService() { return (CCryGameSpyLobbyService*)m_pService; }

					void								DispatchProfileOnlineStatus(GPProfile profile, EOnlineState state);
					void								DispatchFriendIDEvent( ECryLobbySystemEvent event, GPProfile profile );
					void								UpdateProfile(GPConnection* pGPConnection, GPProfile profile, uint32 timestamp);

					void								ProcessActiveFriendStateChanges();

	static	void								GPRecvBuddyRequestCallback(GPConnection* pGPConnection, void* pArg, void* pParam);
	static	void								GPRecvBuddyStatusCallback(GPConnection* pGPConnection, void* pArg, void* pParam);
	static	void								GPRecvBuddyMessageCallback(GPConnection* pGPConnection, void* pArg, void* pParam);
	static	void								GPRecvBuddyUTMCallback(GPConnection* pGPConnection, void* pArg, void* pParam);
	static	void								GPRecvGameInviteCallback(GPConnection* pGPConnection, void* pArg, void* pParam);
	static	void								GPRecvBuddyAuthCallback(GPConnection* pGPConnection, void* pArg, void* pParam);
	static	void								GPRecvBuddyRevokeCallback(GPConnection* pGPConnection, void* pArg, void* pParam);

	static	void								GPGetReverseBuddyListCallback( GPConnection* pGPConnection, void* pArg, void* pParam );
	static	void								GPGetInfoCallback(GPConnection* pGPConnection, void* pArg, void* pParam);

	TProfileMap									m_ProfileMap;
	std::queue< GPProfile >			m_activeProfileQueue;
	STask												m_task[MAX_FRIENDS_TASKS];
	GPConnection*								m_pGPConnection;
	GPProfile										m_GPProfile;
};

#endif // USE_CRYLOBBY_GAMESPY
#endif // __CRYGAMESPYFRIENDS_H__